package com.suieqian.filter.logFile;

import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.NamedThreadLocal;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.lang.Nullable;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;
import java.util.Random;

/**
 * @author lirch
 */
@Slf4j
public class MyInterceptor implements HandlerInterceptor {
    private static final ThreadLocal<Long> startTimeThreadLocal = new NamedThreadLocal<Long>("MyInterceptor StartTime");

    /**
     * 获取随机数
     */
    private String getRandom() {
        Random ran = new Random();
        int random = ran.nextInt(10000);
        return String.valueOf(random);
    }

    private String getParameterMap(Map<String, String[]> parameterMap) {
        StringBuilder sb = new StringBuilder();
        if (parameterMap != null) {
            for (String key : parameterMap.keySet()) {
                String _val = "";
                String[] strings = parameterMap.get(key);
                if (strings != null && strings.length > 0) {
                    _val = StringUtils.join(strings, ",");
                }
                sb.append(key).append("=").append(_val).append("&");
            }
            String param = sb.toString();
            if (StringUtils.isNotBlank(param)) {
                param = param.substring(0, param.length() - 1);
            }
            return param;
        }
        return sb.toString();
    }

    /**
     * 该方法将在请求处理之前进行调用。
     * 多个Interceptor，然后SpringMVC会根据声明的前后顺序一个接一个的执行，而且所有的Interceptor中的preHandle方法都会在
     * Controller方法调用之前调用。SpringMVC的这种Interceptor链式结构也是可以进行中断的，这种中断方式是令preHandle的返
     * 回值为false，当preHandle的返回值为false的时候整个请求就结束了。
     *
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        long startTime = System.currentTimeMillis();

        String contentType = request.getHeader("Content-Type");
        String uid = request.getHeader("operatorId");
        if (StrUtil.isBlank(uid)) {
            uid = request.getParameter("operatorId");
        }

        if (HttpMethod.GET.equals(request.getMethod().toUpperCase())) {
            request.setAttribute("myInterceptor_startTime", startTime);
            String requestId = startTime + getRandom();
            request.setAttribute("myInterceptor_requestId", requestId);
            log.info("请求开始requestId:" + requestId + "===地址:" + request.getRequestURL().toString() + " uid:" + uid);
            log.info("请求开始requestId:" + requestId + "===x-manage-session:" + request.getHeader("x-manage-session"));
            log.info("请求开始requestId:" + requestId + "===" + request.getMethod() + "参数:" + getParameterMap(request.getParameterMap()));
        } else if (StrUtil.isNotBlank(contentType) && contentType.indexOf(MediaType.APPLICATION_JSON_VALUE) == -1 && HttpMethod.POST.equals(request.getMethod().toUpperCase())) {
            request.setAttribute("myInterceptor_startTime", startTime);
            String requestId = startTime + getRandom();
            request.setAttribute("myInterceptor_requestId", requestId);
            log.info("请求开始requestId:" + requestId + "===地址:" + request.getRequestURL().toString() + " uid:" + uid);
            log.info("请求开始requestId:" + requestId + "===x-manage-session:" + request.getHeader("x-manage-session"));
            log.info("请求开始requestId:" + requestId + "===" + request.getMethod() + "参数:" + new RequestWrapper(request).getBodyString(request));
        }
        return true;
    }

    /**
     * 在当前请求进行处理之后，也就是Controller 方法调用之后执行，但是它会在DispatcherServlet
     * 进行视图返回渲染之前被调用，所以我们可以在这个方法中对Controller 处理之后的ModelAndView 对象进行操作。
     *
     * @param request
     * @param response
     * @param handler
     * @param modelAndView
     * @throws Exception
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
//        long startTime = (Long) request.getAttribute("myInterceptor_startTime");
//        String requestId = request.getAttribute("myInterceptor_requestId").toString();
//        long endTime = System.currentTimeMillis();
//        long executeTime = endTime - startTime;
//        if (handler instanceof HandlerMethod) {
//            ResponseWrapper wrapperResponse = new ResponseWrapper(response);
//            wrapperResponse.setContentType(response.getContentType());
//            //获取返回值
//            String result = "";
//            byte[] content = wrapperResponse.getContent();
//            if (content.length > 0) {
//                result = new String(content, "UTF-8");
//                //把返回值输出到客户端
//                ServletOutputStream out = response.getOutputStream();
//                if (result != null) {
//                    //这句必须加 不加会导致请求保留 响应时间非常久 返回需保持两个response长度保持一致
//                    response.setContentLength(result.getBytes().length);
//                    out.write(result.getBytes());
//                }
//                out.flush();
//                out.close();
//            }
//            log.info("请求结束requestId:" + requestId + "==用时:" + executeTime + "ms===返回值:" + result);
//        }
    }

    /**
     * 该方法将在整个请求结束之后，也就是在DispatcherServlet 渲染了对应的视图之后执行。这个方法的主要作用是用于进行资源清理工作的。
     *
     * @param request
     * @param response
     * @param handler
     * @param ex
     * @throws Exception
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
        startTimeThreadLocal.remove();
    }
}
